﻿using System;
using System.Collections.Generic;
using System.Windows.Threading;

namespace PhantomMvvmToolkit
{
    /// <summary>
    /// This IDisposable object will force any PropertyChanged or CanExecuteChanged to fire in a raise though the 
    /// ThreadSafeEvents.UiThreadDispatcher until it is disposed.
    /// </summary>
    public class ThreadSafeEvents : IDisposable
    {
        /// <summary>
        /// Used to store the active ThreadSafeEvents objects.
        /// </summary>
        internal static readonly List<ThreadSafeEvents> InThreadSafe = new List<ThreadSafeEvents>();

        /// <summary>
        /// Holder for the uiThreadDispatcher property.
        /// </summary>
        private static Dispatcher uiThreadDispatcher;

        /// <summary>
        /// Initializes a new instance of the <see cref="ThreadSafeEvents"/> class.
        /// </summary>
        /// <param name="notifyChanged">The object that needs to be cross thread safe.</param>
        /// <param name="onlyRootObject">If set to <c>true</c> this will only make the the provided object cross thread safe.</param>
        internal ThreadSafeEvents(object notifyChanged, bool onlyRootObject)
        {
            InThreadSafe.Add(this);

            Children = new List<ThreadSafeEvents>();
            SafeObject = notifyChanged;

            if (!onlyRootObject)
            {
                var realType = notifyChanged.GetType();
                foreach (var prop in realType.GetProperties())
                {
                    var value = prop.GetValue(notifyChanged, null);
                    if (value != null && (value is IPhantomCommand || value is IPhantomNotifyPropertyChanged))
                        this.Children.Add(new ThreadSafeEvents(value, false));
                }
            }
        }

        /// <summary>
        /// Gets or sets the UI thread dispatcher.
        /// </summary>
        /// <value>The UI thread dispatcher.</value>
        public static Dispatcher UiThreadDispatcher
        {
            get
            {
                if (uiThreadDispatcher == null)
                    throw new InvalidOperationException(
                        "To use the Thread Safe Functionality you must put \"ThreadSafeEvents.UiThreadDispatcher = RootVisual.Dispatcher;\" in app.xaml.cs in the Application_Startup method.");

                return uiThreadDispatcher;
            }

            set
            {
                uiThreadDispatcher = value;
            }
        }

        /// <summary>
        /// Gets the safe object.
        /// </summary>
        /// <value>The safe object.</value>
        internal object SafeObject { get; private set; }

        /// <summary>
        /// Gets or sets the children that need to be disposed of when this is disposed.
        /// </summary>
        /// <value>The children.</value>
        private List<ThreadSafeEvents> Children { get; set; }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            InThreadSafe.Remove(this);

            foreach (var child in this.Children)
            {
                child.Dispose();
            }
        }
    }
}
